home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / HTMLEditorKit.java < prev    next >
Text File  |  1998-06-30  |  32KB  |  1,209 lines

  1. /*
  2.  * @(#)HTMLEditorKit.java    1.30 98/04/12
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing.text.html;
  21.  
  22. import java.awt.*;
  23. import java.awt.event.*;
  24. import java.io.*;
  25. import java.net.MalformedURLException;
  26. import java.net.URL;
  27. import com.sun.java.swing.Action;
  28. import com.sun.java.swing.text.*;
  29. import com.sun.java.swing.*;
  30. import com.sun.java.swing.event.*;
  31. import java.util.*;
  32.  
  33. /**
  34.  * This is the default implementation of html editing
  35.  * functionality.  The primary goal with this is to
  36.  * be small, but flexible.  It is not intended to be
  37.  * an all singing and all dancing html implementation.
  38.  * This is provided to meet more modest needs, with
  39.  * the idea that more substantial needs can be met 
  40.  * with alternative implementations.
  41.  *
  42.  * @author  Timothy Prinzing
  43.  * @author  Makarand Gokhale
  44.  * @version 1.30 04/12/98
  45.  */
  46. public class HTMLEditorKit extends StyledEditorKit {
  47.    
  48.     /**
  49.      * Constructs an HTMLEditorKit, creates a StyleContext,
  50.      * and loads the style sheet.
  51.      */
  52.     public HTMLEditorKit() {
  53.     styleContext = new StyleContext();
  54.     loadStyleSheet(styleContext);
  55.     }
  56.  
  57.     /**
  58.      * Create a copy of the editor kit.  This
  59.      * allows an implementation to serve as a prototype
  60.      * for others, so that they can be quickly created.
  61.      *
  62.      * @return the copy
  63.      */
  64.     public Object clone() {
  65.     return new HTMLEditorKit();
  66.     }
  67.  
  68.     /**
  69.      * Get the MIME type of the data that this
  70.      * kit represents support for.  This kit supports
  71.      * the type <code>text/html</code>.
  72.      *
  73.      * @return the type
  74.      */
  75.     public String getContentType() {
  76.     return "text/html";
  77.     }
  78.  
  79.     /**
  80.      * Fetch a factory that is suitable for producing 
  81.      * views of any models that are produced by this
  82.      * kit.  
  83.      *
  84.      * @return the factory
  85.      */
  86.     public ViewFactory getViewFactory() {
  87.     return new HTMLFactory();
  88.     }
  89.  
  90.     /**
  91.      * Create an uninitialized text storage model
  92.      * that is appropriate for this type of editor.
  93.      *
  94.      * @return the model
  95.      */
  96.     public Document createDefaultDocument() {
  97.     StyledDocument doc = new HTMLDocument(styleContext);
  98.  
  99.     return doc;
  100.     }
  101.  
  102.     /**
  103.      * Create and initialize a model from the given
  104.      * stream which is expected to be in a format appropriate
  105.      * for this kind of editor.  This is implemented to read
  106.      * html 3.2 text.
  107.      * 
  108.      * @param in  The stream to read from
  109.      * @param doc The destination for the insertion.
  110.      * @param pos The location in the document to place the
  111.      *   content.
  112.      * @exception IOException on any I/O error
  113.      * @exception BadLocationException if pos represents an invalid
  114.      *   location within the document.
  115.      */
  116.     public void read(Reader in, Document doc, int pos) throws IOException, BadLocationException {
  117.  
  118.     if (doc instanceof HTMLDocument) {
  119.         HTMLDocument hdoc = (HTMLDocument) doc;
  120.         Parser p = getParser();
  121.         ParserCallback receiver = hdoc.getReader(pos);
  122.         p.parse(in, receiver);
  123.         receiver.flush();
  124.     } else {
  125.         super.read(in, doc, pos);
  126.     }
  127.     }
  128.  
  129.     /**
  130.      * Write content from a document to the given stream
  131.      * in a format appropriate for this kind of content handler.
  132.      * 
  133.      * @param out  The stream to write to
  134.      * @param doc The source for the write.
  135.      * @param pos The location in the document to fetch the
  136.      *   content.
  137.      * @param len The amount to write out.
  138.      * @exception IOException on any I/O error
  139.      * @exception BadLocationException if pos represents an invalid
  140.      *   location within the document.
  141.      */
  142.     public void write(Writer out, Document doc, int pos, int len) 
  143.     throws IOException, BadLocationException {
  144.  
  145.     if (doc instanceof StyledDocument) {
  146.         try {
  147.         HTMLWriter w = new HTMLWriter();
  148.         w.write(out, (StyledDocument)doc);
  149.         } catch (Throwable e) {
  150.         throw new IOException(e.getMessage());
  151.         }
  152.     } else {
  153.         super.write(out, doc, pos, len);
  154.     }
  155.     }
  156.  
  157.     /**
  158.      * Called when the kit is being installed into the
  159.      * a JEditorPane. 
  160.      *
  161.      * @param c the JEditorPane
  162.      */
  163.     public void install(JEditorPane c) {
  164.     c.addMouseListener(linkHandler);
  165.     super.install(c);
  166.     }
  167.  
  168.     /**
  169.      * Called when the kit is being removed from the
  170.      * JEditorPane.  This is used to unregister any 
  171.      * listeners that were attached.
  172.      *
  173.      * @param c the JEditorPane
  174.      */
  175.     public void deinstall(JEditorPane c) {
  176.     c.removeMouseListener(linkHandler);
  177.     super.deinstall(c);
  178.     }
  179.  
  180.     /**
  181.      * Default Cascading Style Sheet file that sets
  182.      * up the tag views.
  183.      */
  184.     public static final String DEFAULT_CSS = "default.css";
  185.  
  186.     /**
  187.      * Load in the default.css file.
  188.      */
  189.     private void loadStyleSheet(StyleContext sc) {
  190.     InputStream is = this.getClass().getResourceAsStream(DEFAULT_CSS);
  191.     if (is == null) 
  192.         System.out.println("HTMLEditorKit.loadStyleSheet: " + DEFAULT_CSS + " file not found");
  193.     else {
  194.       //
  195.       // Create the StyleReader and call it.
  196.       //
  197.       StyleReader stylereader = new StyleReader(sc);
  198.       stylereader.read(sc, 0, is);
  199.     }
  200.  
  201.     }
  202.  
  203.     /**
  204.      * Fetches the command list for the editor.  This is
  205.      * the list of commands supported by the superclass
  206.      * augmented by the collection of commands defined
  207.      * locally for style operations.
  208.      *
  209.      * @return the command list
  210.      */
  211.     public Action[] getActions() {
  212.     return TextAction.augmentList(super.getActions(), this.defaultActions);
  213.     }
  214.  
  215.     /**
  216.      * Fetch the parser to use for reading html streams.
  217.      * This can be reimplemented to provide a different
  218.      * parser.  The default implementation is loaded dynamically
  219.      * to avoid the overhead of loading the default parser if
  220.      * it's not used.  The default parser is based upon
  221.      * <a href="http://suntest.sun.com/JavaCC/index.html">JavaCC</a>,
  222.      * with the grammar defined in the file <code>html-3.2.jj</code>.
  223.      * One can replace the parser using a customized grammar, or
  224.      * replace the parser with one that doesn't use
  225.      * the JavaCC parser generator.
  226.      */
  227.     protected Parser getParser() {
  228.     if (defaultParser == null) {
  229.         try {
  230.                 Class c = Class.forName("com.sun.java.swing.text.html.html32$DefaultParser");
  231.                 defaultParser = (Parser) c.newInstance();
  232.         } catch (Throwable e) {
  233.         }
  234.     }
  235.     return defaultParser;
  236.     }
  237.  
  238.     // --- variables ------------------------------------------
  239.  
  240.     private StyleContext styleContext = null;
  241.     private MouseListener linkHandler = new LinkController();
  242.     private static Parser defaultParser = null;
  243.  
  244.     /**
  245.      * Class to watch the associated component and fire
  246.      * hyperlink events on it when appropriate.
  247.      */
  248.     public static class LinkController extends MouseAdapter {
  249.  
  250.     /**
  251.          * Called for a mouse click event.
  252.      * If the component is read-only (ie a browser) then 
  253.      * the clicked event is used to drive an attempt to
  254.      * follow the reference specified by a link.
  255.      *
  256.      * @param e the mouse event
  257.      * @see MouseListener#mouseClicked
  258.      */
  259.         public void mouseClicked(MouseEvent e) {
  260.         JEditorPane editor = (JEditorPane) e.getSource();
  261.         if (! editor.isEditable()) {
  262.         Point pt = new Point(e.getX(), e.getY());
  263.         int pos = editor.viewToModel(pt);
  264.         if (pos >= 0) {
  265.             activateLink(pos, editor);
  266.         }
  267.         }
  268.     }
  269.  
  270.     /**
  271.      * Calls linkActivated on the associated JEditorPane
  272.      * if the given position represents a link.
  273.          *
  274.          * @param pos the position
  275.          * @param html the editor pane
  276.      */
  277.         protected final void activateLink(int pos, JEditorPane html) {
  278.         Document doc = html.getDocument();
  279.         if (doc instanceof StyledDocument) {
  280.         StyledDocument sdoc = (StyledDocument) doc;
  281.         Element e = sdoc.getCharacterElement(pos);
  282.         AttributeSet a = e.getAttributes();
  283.         String href = (String) a.getAttribute("href");
  284.         if (href != null) {
  285.             URL u;
  286.             try {
  287.                 u = new URL(html.getPage(), href);
  288.             } catch (MalformedURLException m) {
  289.                 u = null;
  290.             }
  291.             HyperlinkEvent linkEvent = 
  292.                 new HyperlinkEvent(html, HyperlinkEvent.EventType.ACTIVATED, u);
  293.             html.fireHyperlinkUpdate(linkEvent);
  294.         }
  295.         }
  296.     }
  297.     }
  298.  
  299.     /**
  300.      * Interface to be supported by the parser.  This enables
  301.      * providing a different parser while reusing some of the
  302.      * implementation provided by this editor kit.
  303.      */
  304.     /*public*/ interface Parser {
  305.  
  306.     /**
  307.      * Parse the given stream and drive the given callback 
  308.      * with the results of the parse.  This method should
  309.      * be implemented to be thread-safe.
  310.      */
  311.     public void parse(Reader r, ParserCallback cb) throws IOException;
  312.  
  313.     }
  314.  
  315.     /**
  316.      * The result of parsing drives these callback methods.
  317.      * The open and close actions should be balanced.  The
  318.      * <code>flush</code> method will be the last method
  319.      * called, to give the receiver a chance to flush any
  320.      * pending data into the document.
  321.      */
  322.     /*public*/ static class ParserCallback {
  323.  
  324.     /* PENDING(prinz) The following is what the parser 
  325.      * communication is expected to look like shortly.
  326.      * We intend to replace the default parser as well.
  327.      *
  328.      * public void flush() throws BadLocationException {
  329.      * }
  330.      *
  331.      * public void pcdata(String data) {
  332.      * }
  333.      *
  334.      * public void comment(String data) {
  335.      * }
  336.      *
  337.      * public void startTag(Constants.Tag t, AttributeSet a) {
  338.      * }
  339.      *
  340.      * public void endTag(Constants.Tag t) {
  341.      * }
  342.      * 
  343.      * public void simpleTag(Constants.Tag t, AttributeSet a) {
  344.      * }
  345.      */
  346.  
  347.         public void flush() throws BadLocationException {
  348.     }
  349.  
  350.         public void pcdataAction(String data) {
  351.     }
  352.  
  353.         public void whitespaceAction(String data) {
  354.     }
  355.  
  356.         public void attributeAction(String name, String value) {
  357.     }
  358.  
  359.         public void blockOpenAction(String tag) {
  360.     }
  361.  
  362.         public void blockCloseAction(String tag) {
  363.     }
  364.  
  365.         public void aOpenAction() {
  366.     }
  367.  
  368.         public void aCloseAction() {
  369.     }
  370.  
  371.         public void imgAction() {
  372.     }
  373.  
  374.         public void hrAction() {
  375.     }
  376.  
  377.         public void fontOpenAction() {
  378.     }
  379.  
  380.         public void fontCloseAction() {
  381.         }
  382.  
  383.         public void iOpenAction() {
  384.     }
  385.     
  386.         public void iCloseAction() {
  387.     }
  388.     
  389.         public void bOpenAction() {
  390.     }
  391.     
  392.         public void bCloseAction() {
  393.     }
  394.     
  395.         public void uOpenAction() {
  396.     }
  397.     
  398.         public void uCloseAction() {
  399.     }
  400.     
  401.         public void strikeOpenAction() {
  402.     }
  403.     
  404.         public void strikeCloseAction() {
  405.     }
  406.  
  407.         public void liOpenAction() {
  408.     }
  409.     
  410.         public void liCloseAction() {
  411.     }
  412.     
  413.         public void ulOpenAction() {
  414.     }
  415.     
  416.         public void ulCloseAction() {
  417.     }
  418.     
  419.         public void olOpenAction() {
  420.     }
  421.     
  422.         public void olCloseAction() {
  423.     }
  424.     
  425.         public void dlOpenAction() {
  426.     }
  427.     
  428.         public void dlCloseAction() {
  429.     }
  430.     
  431.         public void ddOpenAction() {
  432.     }
  433.     
  434.         public void ddCloseAction() {
  435.     }
  436.     
  437.         public void dtOpenAction() {
  438.     }
  439.     
  440.         public void dtCloseAction() {
  441.     }
  442.     
  443.         public void dirOpenAction() {
  444.     }
  445.     
  446.         public void dirCloseAction() {
  447.     }
  448.     
  449.         public void menuOpenAction() {
  450.     }
  451.     
  452.         public void menuCloseAction() {
  453.     }
  454.  
  455.         public void htmlOpenAction() {
  456.     }
  457.     
  458.         public void htmlCloseAction() {
  459.     }
  460.  
  461.         public void headOpenAction() {
  462.     }
  463.     
  464.         public void headCloseAction() {
  465.     }
  466.  
  467.         public void bodyOpenAction() {
  468.     }
  469.     
  470.         public void bodyCloseAction() {
  471.     }
  472.  
  473.         public void titleOpenAction() {
  474.     }
  475.     
  476.         public void titleCloseAction() {
  477.     }
  478.  
  479.         public void preOpenAction() {
  480.     }
  481.  
  482.         public void preCloseAction() {
  483.     }
  484.  
  485.         public void ttOpenAction(){
  486.     }
  487.  
  488.         public void ttCloseAction(){
  489.     }
  490.  
  491.         public void bigOpenAction(){
  492.     }
  493.  
  494.         public void bigCloseAction(){
  495.     }
  496.  
  497.         public void smallOpenAction(){
  498.     }
  499.  
  500.         public void smallCloseAction(){
  501.     }
  502.  
  503.         public void blockquoteOpenAction(){
  504.     }
  505.  
  506.         public void blockquoteCloseAction(){
  507.     }
  508.  
  509.         public void emOpenAction(){
  510.     }
  511.  
  512.         public void emCloseAction(){
  513.     }
  514.  
  515.         public void strongOpenAction(){
  516.     }
  517.  
  518.         public void strongCloseAction(){
  519.     }
  520.  
  521.         public void varOpenAction(){
  522.     }
  523.  
  524.         public void varCloseAction(){
  525.     }
  526.  
  527.         public void basefontAction(){
  528.     }
  529.  
  530.         public void brAction(){
  531.     }
  532.  
  533.         public void centerOpenAction(){
  534.     }
  535.  
  536.         public void centerCloseAction(){
  537.     }
  538.  
  539.         public void citeOpenAction(){
  540.     }
  541.  
  542.         public void citeCloseAction(){
  543.     }
  544.  
  545.         public void kbdOpenAction(){
  546.     }
  547.  
  548.         public void kbdCloseAction(){
  549.     }
  550.  
  551.         public void subOpenAction(){
  552.     }
  553.  
  554.         public void subCloseAction(){
  555.     }
  556.  
  557.         public void supOpenAction(){
  558.     }
  559.  
  560.         public void supCloseAction(){
  561.     }
  562.  
  563.         public void dfnOpenAction(){
  564.     }
  565.  
  566.         public void dfnCloseAction(){
  567.     }
  568.  
  569.         public void codeOpenAction(){
  570.     }
  571.  
  572.         public void codeCloseAction(){
  573.     }
  574.  
  575.         public void sampOpenAction(){
  576.     }
  577.  
  578.         public void sampCloseAction(){
  579.     }
  580.  
  581.         public void addressOpenAction(){
  582.     }
  583.  
  584.         public void addressCloseAction(){
  585.     }
  586.  
  587.         public void divOpenAction(){
  588.     }
  589.  
  590.         public void divCloseAction(){
  591.     }
  592.  
  593.         public void mapOpenAction(){
  594.     }
  595.  
  596.         public void mapCloseAction(){
  597.     }
  598.  
  599.         public void areaAction(){
  600.     }
  601.  
  602.         public void linkAction(){
  603.     }
  604.  
  605.         public void paramAction(){
  606.     }
  607.  
  608.         public void inputAction(){
  609.     }
  610.  
  611.         public void appletOpenAction(){
  612.     }
  613.  
  614.         public void appletCloseAction(){
  615.     }
  616.  
  617.         public void formOpenAction(){
  618.     }
  619.  
  620.         public void formCloseAction(){
  621.     }
  622.  
  623.         public void selectOpenAction(){
  624.     }
  625.  
  626.         public void selectCloseAction(){
  627.     }
  628.  
  629.         public void optionOpenAction(){
  630.     }
  631.  
  632.         public void optionCloseAction(){
  633.     }
  634.  
  635.         public void textareaOpenAction(){
  636.     }
  637.  
  638.         public void textareaCloseAction(){
  639.     }
  640.  
  641.         public void tableOpenAction(){
  642.     }
  643.  
  644.         public void tableCloseAction(){
  645.     }
  646.  
  647.         public void trOpenAction(){
  648.     }
  649.  
  650.         public void trCloseAction(){
  651.     }
  652.  
  653.         public void thOpenAction(){
  654.     }
  655.  
  656.         public void thCloseAction(){
  657.     }
  658.  
  659.         public void tdOpenAction(){
  660.     }
  661.  
  662.         public void tdCloseAction(){
  663.     }
  664.  
  665.         public void captionOpenAction(){
  666.     }
  667.  
  668.         public void captionCloseAction(){
  669.     }
  670.  
  671.         public void isindexAction(){
  672.     }
  673.  
  674.         public void baseAction(){
  675.     }
  676.  
  677.         public void metaAction(){
  678.     }
  679.  
  680.         public void styleOpenAction(){
  681.     }
  682.  
  683.         public void styleCloseAction(){
  684.     }
  685.  
  686.         public void scriptOpenAction(){
  687.     }
  688.  
  689.         public void scriptCloseAction(){
  690.     }
  691.  
  692.     }
  693.  
  694.     /**
  695.      * A factory to build view fragments for html.
  696.      */
  697.     public static class HTMLFactory implements ViewFactory {
  698.     
  699.     /**
  700.      * Creates a view from an element.
  701.      *
  702.      * @param elem the element
  703.      * @return the view
  704.      */
  705.         public View create(Element elem) {
  706.         String kind = elem.getName();
  707.         if (kind != null) {
  708.         String ikind = kind.intern();
  709.         if (ikind == AbstractDocument.ContentElementName) {
  710.             // text content
  711.             return new LabelView(elem);
  712.         } else if ((ikind == AbstractDocument.ParagraphElementName) || (ikind == Constants.DT)) {
  713.             // paragraph
  714.             return new ParagraphView(elem);
  715.         } else if ((ikind == Constants.MENU) || (ikind == Constants.DIR) ||
  716.                (ikind == Constants.UL)   || (ikind == Constants.OL)) {
  717.             return new ListView(elem);
  718.         } else if (ikind == AbstractDocument.SectionElementName) {
  719.             return new BodyView(elem, View.Y_AXIS);
  720.         } else if (ikind == Constants.PRELINE) {
  721.             return new LineView(elem);
  722.         } else if ((ikind == Constants.LI) || (ikind == Constants.DL)  ||
  723.                (ikind == Constants.DD) || (ikind == Constants.PRE)) {
  724.             // vertical box
  725.             return new HTMLBoxView(elem, View.Y_AXIS);
  726.         } else if ((ikind == Constants.FORM)) {
  727.             // horizontal box
  728.             return new HTMLBoxView(elem, View.X_AXIS);
  729.         } else if (ikind==StyleConstants.ComponentElementName) {
  730.             return new ComponentView(elem);
  731.         } else if (ikind==Constants.IMG) {
  732.             return new ImageView(elem);
  733.         } else if (ikind == Constants.HR) {
  734.             return new HRuleView(elem);
  735.         } else if (ikind == Constants.TABLE ) {
  736.             return new TableView(elem);
  737.         }
  738.         }
  739.  
  740.         // don't know how to build this....
  741.         return null;
  742.     }
  743.  
  744.     }
  745.  
  746.     // --- Action implementations ------------------------------
  747.  
  748. /** The bold action identifier
  749. */
  750.     public static final String    BOLD_ACTION = "html-bold-action";
  751. /** The italic action identifier
  752. */
  753.     public static final String    ITALIC_ACTION = "html-italic-action";
  754. /** The paragraph left indent action identifier
  755. */
  756.     public static final String    PARA_INDENT_LEFT = "html-para-indent-left";
  757. /** The paragraph right indent action identifier
  758. */
  759.     public static final String    PARA_INDENT_RIGHT = "html-para-indent-right";
  760. /** The  font size increase to next value action identifier
  761. */
  762.     public static final String    FONT_CHANGE_BIGGER = "html-font-bigger";
  763. /** The font size decrease to next value action identifier
  764. */
  765.     public static final String    FONT_CHANGE_SMALLER = "html-font-smaller";
  766. /** The Color choice action identifier
  767.      The color is passed as an argument
  768. */
  769.     public static final String    COLOR_ACTION = "html-color-action";
  770. /** The logical style choice action identifier
  771.      The logical style is passed in as an argument
  772. */
  773.     public static final String    LOGICAL_STYLE_ACTION = "html-logical-style-action";
  774.     /**
  775.      * Align images at the top.
  776.      */
  777.     public static final String    IMG_ALIGN_TOP = "html-image-align-top";
  778.  
  779.     /**
  780.      * Align images in the middle.
  781.      */
  782.     public static final String    IMG_ALIGN_MIDDLE = "html-image-align-middle";
  783.  
  784.     /**
  785.      * Align images at the bottom.
  786.      */
  787.     public static final String    IMG_ALIGN_BOTTOM = "html-image-align-bottom";
  788.  
  789.     /**
  790.      * Align images at the border.
  791.      */
  792.     public static final String    IMG_BORDER = "html-image-border";
  793.  
  794.     // --- Action implementations ---------------------------------
  795.  
  796.     private static final Action[] defaultActions = {
  797.     new HTMLBoldAction(),
  798.     new HTMLItalicAction(),
  799.     new FontSizeChangeAction(FONT_CHANGE_BIGGER, true),
  800.     new FontSizeChangeAction(FONT_CHANGE_SMALLER, false),
  801.     new ParagraphIndentAction(PARA_INDENT_LEFT, true),
  802.     new ParagraphIndentAction(PARA_INDENT_RIGHT, false),
  803.     new ImgAlignAction(IMG_ALIGN_TOP, ImageView.TOP),
  804.     new ImgAlignAction(IMG_ALIGN_MIDDLE, ImageView.MIDDLE),
  805.     new ImgAlignAction(IMG_ALIGN_BOTTOM, ImageView.BOTTOM),
  806.     new ImgBorderAction(IMG_BORDER),
  807.     new ForegroundAction(COLOR_ACTION, Color.black),
  808.     new LogicalStyleAction("Normal")
  809.     };
  810.  
  811.     static abstract class HtmlAction extends StyledTextAction {
  812.  
  813.     HtmlAction(String nm) {
  814.         super(nm);
  815.     }
  816.  
  817.     /**
  818.      * Applies the given attributes to character 
  819.      * content.   The attributes are applied to a range. The range
  820.      * must be within current selection
  821.      * If the the start and end point are the same then 
  822.      * the attributes are applied to
  823.      * the input attribute set which defines the attributes
  824.      * for any new text that gets inserted.
  825.      *
  826.      * @param attr the attributes
  827.      * @param start The starting position for a range
  828.      * @param end   the ending position for a range
  829.      * @param replace if true, then replace the existing attributes first
  830.      */
  831.         protected final void setCharacterAttributes(JEditorPane editor, 
  832.                           AttributeSet attr, int start,
  833.                           int end, boolean replace) {
  834.         if(start > end) {
  835.         int tmp = end;
  836.         end = start;
  837.         start = tmp;
  838.         }
  839.         int p0 = editor.getSelectionStart();
  840.         int p1 = editor.getSelectionEnd();
  841.         if(start < p0) start = p0;
  842.         if(end > p1 ) end = p1;
  843.         if (start != end) {
  844.         StyledDocument doc = getStyledDocument(editor);
  845.         doc.setCharacterAttributes(start, end - start, attr, replace);
  846.         } else {
  847.         StyledEditorKit k = getStyledEditorKit(editor);
  848.         MutableAttributeSet inputAttributes = k.getInputAttributes();
  849.         if (replace) {
  850.             inputAttributes.removeAttributes(inputAttributes);
  851.         }
  852.         inputAttributes.addAttributes(attr);
  853.         }
  854.         }
  855.     }
  856.  
  857.  
  858.     /**
  859.      * An action to toggle the bold attribute
  860.      */
  861.     static class HTMLBoldAction extends HtmlAction {
  862.  
  863.     public HTMLBoldAction() {
  864.         super(BOLD_ACTION);
  865.     }
  866.  
  867.       public void actionPerformed(ActionEvent ae) {
  868.         JTextComponent target = getFocusedComponent();
  869.           if (target != null) {
  870.         JEditorPane pane = (JEditorPane) target;
  871.         MutableAttributeSet attr = new SimpleAttributeSet();
  872. // Check if the event source is a toggle button
  873. // If so set or unset bold based on toggle state
  874.         Object o = ae.getSource();
  875.         boolean bSet=true;
  876.         if (o != null && o instanceof JToggleButton)
  877.             bSet = ((JToggleButton)o).isSelected();
  878.         StyleConstants.setBold(attr, bSet);
  879.             setCharacterAttributes(pane, attr, false);
  880.          }
  881.     }
  882.     }
  883.  
  884.     /**
  885.      * An action to toggle the italic attribute
  886.      */
  887.     static class HTMLItalicAction extends HtmlAction {
  888.  
  889.     public HTMLItalicAction() {
  890.         super(ITALIC_ACTION);
  891.     }
  892.  
  893.       public void actionPerformed(ActionEvent ae) {
  894.         JTextComponent target = getFocusedComponent();
  895.           if (target != null) {
  896.         JEditorPane pane = (JEditorPane) target;
  897.         MutableAttributeSet attr = new SimpleAttributeSet();
  898. // Check if the event source is a toggle button
  899. // If so set or unset italic based on toggle state
  900.         Object o = ae.getSource();
  901.         boolean bSet=true;
  902.         if (o != null && o instanceof JToggleButton)
  903.             bSet = ((JToggleButton)o).isSelected();
  904.         StyleConstants.setItalic(attr, bSet);
  905.             setCharacterAttributes(pane,attr, false);
  906.          }
  907.     }
  908.     }
  909.  
  910.  
  911. /**
  912.  *
  913.  * This class supports the font size change action. When the button
  914.  * is clicked on a toolbar it sends an action event to this class
  915.  * This class defines a local variable to specify, whether to increas
  916.  * or decrease the font size. It obtains the next size using HTMLStyleSheet
  917.  *
  918.  * The font size is changed only if there is only one style in effect
  919.  * for a selection. Otherwise the change request is ignored.
  920.  *
  921.  * The following is the order of processing
  922.  * 1.    Check if there is a range of selection.
  923.  * 2. If not set the fontsize for the input
  924.  & 3. If ma range is selected then get all the effective styles
  925.  * 4. If more than one size exists on a Font Ignore change request
  926.  * 5. Otherwise change font
  927.  */
  928.  
  929.  
  930.     static class FontSizeChangeAction extends HtmlAction {
  931.     private boolean bIncrease = true;
  932.  
  933.         FontSizeChangeAction( String key, boolean b) {
  934.         super(key);
  935.         bIncrease = b;
  936.         }
  937.       
  938.     public void actionPerformed(ActionEvent event) {
  939.         JTextComponent target = getFocusedComponent();
  940.         StyleSheet ss = StyleReader.getStyleSheet();
  941.           if (target != null) {
  942.         JEditorPane pane = (JEditorPane) target;
  943.  
  944.         Caret   ip = pane.getCaret();
  945.         int pos = ip.getDot();
  946.         if (ip.getMark()  ==  pos) {
  947. /* ASK TIM HOW TO GET INPUTATTRIBUTES
  948.             AttributeSet as = pane.getInputAttributes();
  949.             */
  950.             AttributeSet as = null;
  951.             if (as != null) {
  952.                   int size = StyleConstants.getFontSize(as);
  953.                   size= (bIncrease?ss.getBigger(size):ss.getSmaller(size));
  954.             MutableAttributeSet attr = new SimpleAttributeSet();
  955.             StyleConstants.setFontSize(attr, size);
  956.             setCharacterAttributes(pane,attr, false);
  957.             }
  958.         }
  959.         else {
  960.             int start = ip.getMark();
  961.             int end = ip.getDot();
  962.             Vector v = getCharacterElements(pane, start,end);
  963.             if (v != null ) {
  964.             int size=-1;
  965.             int nextSize=-1;
  966.             int vSize=v.size();
  967.             if (vSize > 0 ) {
  968.                 boolean bAbort = false;
  969.                 int SizeArray[] = new int[vSize];
  970.                 for (int i=0; i < vSize && !bAbort; i++) {
  971.                 try {
  972.                     AttributeSet as = ((Element)v.elementAt(i)).getAttributes();
  973.                     size= StyleConstants.getFontSize(as);
  974.                     nextSize = (bIncrease?ss.getBigger(size):ss.getSmaller(size));
  975.                     if (size == nextSize) {
  976.                     System.out.println("Bigger/Smaller ABORTING:Size limit reached for element:"+((Element)v.elementAt(i)).getName());
  977.                     bAbort = true;
  978.                     continue;
  979.                     }
  980.                     SizeArray[i] = nextSize;
  981.                 } catch(NullPointerException ee1) {
  982.                     System.out.println("Exception: Cannot find font size for element:"+i);
  983.                     SizeArray[i] = -1;
  984.                     bAbort = true;
  985.                     continue;
  986.                 }
  987.                 }
  988.                 if (!bAbort) {
  989.                 int eStart, eEnd;
  990.                 for (int i=0; i <vSize;i++) {
  991.                     Element elem = (Element)v.elementAt(i);
  992.                     eStart = elem.getStartOffset();
  993.                     eEnd = elem.getEndOffset();
  994.                     if (eStart < start)  eStart = start;
  995.                     if (eEnd > end)  eEnd = end;
  996.                     if (SizeArray[i] > 0) {
  997.                     MutableAttributeSet attr = new SimpleAttributeSet();
  998.                     StyleConstants.setFontSize(attr, SizeArray[i]);
  999.                     setCharacterAttributes(pane,attr,eStart, eEnd, false);
  1000.                     }
  1001.                 }
  1002.                 }
  1003.                 else{
  1004.                 System.out.println("Atleast one size at the limit, Aborting bigger/smaller");
  1005.                 }
  1006.                 }
  1007.               }
  1008.         }
  1009.         }
  1010.     }
  1011.     
  1012.     }
  1013.      
  1014.   /**
  1015.      * ParagraphIndentAction sets a new alignment.  If it is instantiated
  1016.      * with bInc=true then the next alignment will be chosen in order
  1017.      * from left to right.  If bInc=false then the next alignment will be
  1018.      * in the order from right to left.
  1019.      */
  1020.     static class ParagraphIndentAction extends  HtmlAction {
  1021.            boolean    bLeft = false;
  1022.  
  1023.           ParagraphIndentAction(String key, boolean b) {
  1024.         super(key);
  1025.         bLeft = b;
  1026.         }
  1027.       
  1028.         public void actionPerformed(ActionEvent e) {
  1029.         JEditorPane pane = (JEditorPane)getFocusedComponent();
  1030.         if (pane != null) {
  1031.  
  1032.         StyledEditorKit k = getStyledEditorKit(pane);
  1033.         MutableAttributeSet attr =  k.getInputAttributes();
  1034.         if (attr != null) {
  1035.                 int align = StyleConstants.getAlignment(attr);
  1036.                 if (!bLeft) {
  1037.                   if (align == StyleConstants.ALIGN_LEFT) {
  1038.                     align = StyleConstants.ALIGN_CENTER;
  1039.             }
  1040.                      else if (align == StyleConstants.ALIGN_CENTER) {
  1041.                     align = StyleConstants.ALIGN_RIGHT;
  1042.                   }
  1043.             }
  1044.              else {
  1045.                   if (align == StyleConstants.ALIGN_CENTER) {
  1046.                         align = StyleConstants.ALIGN_LEFT;
  1047.                 }
  1048.                   else if (align == StyleConstants.ALIGN_RIGHT) {
  1049.                        align = StyleConstants.ALIGN_CENTER;
  1050.                 }
  1051.                 }
  1052.             StyleConstants.setAlignment(attr, align);
  1053.             setParagraphAttributes(pane,attr, false);
  1054.           }
  1055.         }
  1056.        }
  1057.     }
  1058.  
  1059.     /**
  1060.      * Set the Logical Style on the paragraph.
  1061.      * It  calls the setLogicalStyle method on the document
  1062.      */
  1063.  
  1064.     static class LogicalStyleAction extends HtmlAction {
  1065.     String  styleStr;
  1066.           LogicalStyleAction(String styleStr) {
  1067.         super(LOGICAL_STYLE_ACTION);
  1068.         this.styleStr = styleStr;
  1069.           }
  1070.  
  1071.           public void actionPerformed(ActionEvent e) {
  1072.         JEditorPane editor = getEditor(e);
  1073.         if (editor != null) {
  1074.         String styleName = styleStr;
  1075.         if ((e != null) && (e.getSource() == editor)) {
  1076.             String s = e.getActionCommand();
  1077.             if (s != null)
  1078.                styleName = s;
  1079.         }
  1080.         StyledDocument doc = (StyledDocument) editor.getDocument();
  1081.             Style style = doc.getStyle(styleStr);
  1082.             if (style != null) {
  1083.                 doc.setLogicalStyle(editor.getCaretPosition(),style);
  1084.             }
  1085.         }
  1086.           }
  1087.     }
  1088.  
  1089.  /**
  1090.  * A method to get all the elements in the range
  1091.  */
  1092.     static Vector getCharacterElements(JEditorPane editorpane,int start, int end) {
  1093.     StyledDocument doc = (StyledDocument)editorpane.getDocument();
  1094.       Vector v = new Vector();
  1095.       int pos = start;
  1096.       start= (start> end?end:start);
  1097.       end = (start > end?pos:end);
  1098.       pos = start;
  1099.       while (pos <= end) {
  1100.           Element e = doc.getCharacterElement(pos);
  1101.             if ( e== null) {
  1102.               pos++;
  1103.               continue;
  1104.           }
  1105.           v.addElement(e);
  1106.           if (pos < e.getEndOffset()+1)
  1107.               pos = e.getEndOffset()+1;
  1108.           else
  1109.               pos++;
  1110.         }
  1111.     return v;
  1112.     }
  1113.  
  1114.  
  1115.     /**
  1116.      * An action to align images top, middle, bottom
  1117.      */
  1118.     static class ImgAlignAction extends HtmlAction {
  1119.  
  1120.     String align;
  1121.  
  1122.     /** Create an action to set an image (vertical) alignment.
  1123.         @param  name  The name of this action
  1124.         @param  align  The value of the ALIGN attribute. Correct values
  1125.             are "top", "middle", "bottom" (see consts in ImageView) */
  1126.     public ImgAlignAction(String name, String align) {
  1127.         super(name);
  1128.         this.align = align;
  1129.     }
  1130.  
  1131.      public void actionPerformed(ActionEvent ae) {
  1132.          if(DEBUG)System.out.println("ImgAlignAction: set align to "+align);
  1133.         JTextComponent target = getFocusedComponent();
  1134.           if (target != null) {
  1135.         JEditorPane pane = (JEditorPane) target;
  1136.         MutableAttributeSet attr = new SimpleAttributeSet();
  1137.         attr.addAttribute(Constants.ALIGN,align);
  1138.             setCharacterAttributes(pane, attr, false);
  1139.  
  1140.             //((HTMLDocument)pane.getDocument()).dump(System.out);//$$$$$ TEST
  1141.          }
  1142.      }
  1143.     }
  1144.  
  1145.     /**
  1146.      * Toggle the border on an img.
  1147.      */
  1148.     static class ImgBorderAction extends HtmlAction {
  1149.  
  1150.     public ImgBorderAction(String name) {
  1151.         super(name);
  1152.     }
  1153.  
  1154.      public void actionPerformed(ActionEvent ae) {
  1155.         JEditorPane pane = (JEditorPane) getFocusedComponent();
  1156.         AttributeSet attr = getImageAttributes(pane);
  1157.         if( attr != null ) {
  1158.             // Figure out if it currently has a border:
  1159.             boolean link = attr.isDefined(Constants.HREF);
  1160.             int border = link ?2 :0;  // border is default in link
  1161.             String borderStr = (String) attr.getAttribute(Constants.BORDER);
  1162.             if( borderStr != null ) {
  1163.                  try{
  1164.                      border = Integer.parseInt(borderStr);
  1165.                  }catch( NumberFormatException x ) {
  1166.                  }
  1167.              }
  1168.              
  1169.              // Work out the new 'border' attribute value:
  1170.         MutableAttributeSet newAttr = new SimpleAttributeSet();
  1171.         boolean replace = false;
  1172.              if( border == 0  && !link ) {
  1173.                  if(DEBUG)System.out.println("ImgBorderAction: set border=2");
  1174.                  newAttr.addAttribute(Constants.BORDER,"2");
  1175.              } else if( border != 0 && link ) {
  1176.                  if(DEBUG)System.out.println("ImgBorderAction: set border=0");
  1177.                  newAttr.addAttribute(Constants.BORDER,"0");
  1178.         } else {
  1179.             // need to remove 'border' attribute entirely:
  1180.                  if(DEBUG)System.out.println("ImgBorderAction: reset border");
  1181.             newAttr.addAttributes(attr);
  1182.             newAttr.removeAttribute(Constants.BORDER);
  1183.             replace = true;
  1184.              }
  1185.              
  1186.              // Update/replace attributes:
  1187.              setCharacterAttributes(pane, newAttr, replace);
  1188.         }
  1189.      }
  1190.      
  1191.      /** If an image is selected, return its AttributeSet. */
  1192.      private AttributeSet getImageAttributes( JEditorPane editorpane ) {
  1193.           if (editorpane != null) {
  1194.             int start = editorpane.getSelectionStart();
  1195.             int end = editorpane.getSelectionEnd();
  1196.             if( Math.abs(end-start)==1 ) {
  1197.                 StyledDocument htmldoc = (StyledDocument) editorpane.getDocument();
  1198.                 Element e = htmldoc.getCharacterElement(Math.min(start,end));
  1199.                 if( e != null && e.getName().equals(Constants.IMG) )
  1200.                     return e.getAttributes();
  1201.             }
  1202.         }
  1203.         return null;
  1204.      }
  1205.     }
  1206.     
  1207.     static final boolean DEBUG = false;
  1208. }
  1209.